//
//  TYZBaseRequest.h
//  DrivieSchoolStudents
//
//  Created by TangBin on 2019/5/29.
//  Copyright © 2019 班文政. All rights reserved.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN


/*
 TYZBaseRequest 是所有请求的基类，负责提供和网络请求相关的方法
 */

// [NSError errorWithDomain:TYZRequestValidationErrorDomain code:TYZRequestValidationErrorInvalidStatusCode userInfo:@{NSLocalizedDescriptionKey:@"Invalid status code"}]
// 验证失败的时候
FOUNDATION_EXPORT NSString *const TYZRequestValidationErrorDomain;

NS_ENUM(NSInteger)
{
    TYZRequestValidationErrorInvalidStatusCode = -8, ///< 请求验证错误无效状态代码
    TYZRequestValidationErrorInvalidJSONFormat = -9, ///< 请求验证错误无效的JSON格式
};

// HTTP Request methods.
typedef NS_ENUM(NSInteger, TYZRequestMethod)
{
    TYZRequestMethodGET = 0, ///< get请求方式
    TYZRequestMethodPOST, ///< post请求方式
};

    // 请求数据的类型，可以是表格形式；可以是json格式
typedef NS_ENUM(NSInteger, TYZRequestSerializerType)
{
    TYZRequestSerializerTypeHTTP = 0, ///< 表单提交的格式
    TYZRequestSerializerTypeJSON, ///< 提交的是json数据
};
    
// 返回数据的格式
typedef NS_ENUM(NSInteger, TYZResponseSerializerType)
{
    TYZResponseSerializerTypeHTTP = 0,
    TYZResponseSerializerTypeJSON, ///< 返回的是json格式的数据
    TYZResponseSerializerTypeXML, ///< 返回的是xml格式的数据
};
    
// 请求的级别
typedef NS_ENUM(NSInteger, TYZRequestPriority)
{
    TYZRequestPriorityLow = -4L,
    TYZRequestPriorityDefault = 0,
    TYZRequestPriorityHigh = 4,
};
    
@protocol AFMultipartFormData; // 多表单

typedef void (^AFConstructingBlock)(id<AFMultipartFormData> formData); // 实现多表单
typedef void (^AFURLSessionTaskProgressBlock)(NSProgress *); // 进度

@class TYZBaseRequest;

// 完成后的调用
typedef void(^TYZRequestCompletionBlock)(__kindof TYZBaseRequest *request);


@interface TYZBaseRequest : NSObject

#pragma mark - Request and Response Information
///=============================================================================
/// @name Request and Response Information
///=============================================================================

///  The underlying NSURLSessionTask.
///
///  @warning This value is actually nil and should not be accessed before the request starts.
@property (nonatomic, strong, readonly) NSURLSessionTask *requestTask;

///  Shortcut for `requestTask.currentRequest`.
@property (nonatomic, strong, readonly) NSURLRequest *currentRequest;

///  Shortcut for `requestTask.originalRequest`.
@property (nonatomic, strong, readonly) NSURLRequest *originalRequest;

///  Shortcut for `requestTask.response`.
@property (nonatomic, strong, readonly) NSHTTPURLResponse *response;

///  The response status code.
@property (nonatomic, readonly) NSInteger responseStatusCode;

///  The response header fields.
@property (nonatomic, strong, readonly, nullable) NSDictionary *responseHeaders;

///  The raw data representation of response. Note this value can be nil if request failed.
@property (nonatomic, strong, readonly, nullable) NSData *responseData;

///  The string representation of response. Note this value can be nil if request failed.
@property (nonatomic, strong, readonly, nullable) NSString *responseString;

///  This serialized response object. The actual type of this object is determined by
///  `YTKResponseSerializerType`. Note this value can be nil if request failed.
///
///  @discussion If `resumableDownloadPath` and DownloadTask is using, this value will
///              be the path to which file is successfully saved (NSURL), or nil if request failed.
@property (nonatomic, strong, readonly, nullable) id responseObject;

///  If you use `YTKResponseSerializerTypeJSON`, this is a convenience (and sematic) getter
///  for the response object. Otherwise this value is nil.
@property (nonatomic, strong, readonly, nullable) id responseJSONObject;

/**
 上层使用，可能需要，转成model
 */
@property (nonatomic, strong, nullable) id responseModelObject;

///  This error can be either serialization error or network error. If nothing wrong happens
///  this value will be nil.
@property (nonatomic, strong, readonly, nullable) NSError *error;

///  返回请求任务的已取消状态。
@property (nonatomic, readonly, getter=isCancelled) BOOL cancelled;

///  执行请求任务的状态。
@property (nonatomic, readonly, getter=isExecuting) BOOL executing;


#pragma mark - Request Configuration
///=============================================================================
/// @name Request Configuration
///=============================================================================

///  Tag can be used to identify request. Default value is 0.
@property (nonatomic) NSInteger tag;

///  The userInfo can be used to store additional info about the request. Default is nil.
@property (nonatomic, strong, nullable) NSDictionary *userInfo;

///  The success callback. Note if this value is not nil and `requestFinished` delegate method is
///  also implemented, both will be executed but delegate method is first called. This block
///  will be called on the main queue.
@property (nonatomic, copy, nullable) TYZRequestCompletionBlock successCompletionBlock;

///  The failure callback. Note if this value is not nil and `requestFailed` delegate method is
///  also implemented, both will be executed but delegate method is first called. This block
///  will be called on the main queue.
@property (nonatomic, copy, nullable) TYZRequestCompletionBlock failureCompletionBlock;

///  This can be use to construct HTTP body when needed in POST request. Default is nil.
@property (nonatomic, copy, nullable) AFConstructingBlock constructingBodyBlock;

///  This value is used to perform resumable download request. Default is nil.
///
///  @discussion NSURLSessionDownloadTask is used when this value is not nil.
///              The exist file at the path will be removed before the request starts. If request succeed, file will
///              be saved to this path automatically, otherwise the response will be saved to `responseData`
///              and `responseString`. For this to work, server must support `Range` and response with
///              proper `Last-Modified` and/or `Etag`. See `NSURLSessionDownloadTask` for more detail.
/// 下载文件的时候，暂停，取消的情况下，取消的时候的下载信息的保存位置
@property (nonatomic, strong, nullable) NSString *resumableDownloadPath;

///  You can use this block to track the download progress. See also `resumableDownloadPath`.
@property (nonatomic, copy, nullable) AFURLSessionTaskProgressBlock resumableDownloadProgressBlock;

///  The priority of the request. Effective only on iOS 8+. Default is `YTKRequestPriorityDefault`.
@property (nonatomic, assign) TYZRequestPriority requestPriority;

/**
 设置请求完成后的，回调block

 @param success 成功block
 @param failure 失败block
 */
- (void)setCompletionBlockWithSuccess:(nullable TYZRequestCompletionBlock)success
                              failure:(nullable TYZRequestCompletionBlock)failure;

/**
 清空 成功和失败的block
 */
- (void)clearCompletionBlock;



#pragma mark - Request Action
///=============================================================================
/// @name Request Action
///=============================================================================

///  Append self to request queue and start the request.
- (void)start;

///  Remove self from request queue and cancel the request.
- (void)stop;

///  Convenience method to start the request with block callbacks.
- (void)startWithCompletionBlockWithSuccess:(nullable TYZRequestCompletionBlock)success
                                    failure:(nullable TYZRequestCompletionBlock)failure;


#pragma mark - Subclass Override
///=============================================================================
/// @name Subclass Override
///=============================================================================

///  Called on background thread after request succeded but before switching to main thread. Note if 缓存是否要处理 做缓存处理用的，有的时候，某一个接口的数据更新没有那么快，按时不会更新，需要缓存在本地
///  cache is loaded, this method WILL be called on the main thread, just like `requestCompleteFilter`.
- (void)requestCompletePreprocessor;

/**
 Called on background thread after request failed but before switching to not main thread. See also
 */
- (void)requestFailedPreprocessor;

/**
 Called on the  not main thread after request succeeded. 是否要过滤
 可以做进一步处理
 */
- (void)requestCompleteFilter;

///  Called on the main thread when request failed.
- (void)requestFailedFilter;

///  The baseURL of request. This should only contain the host part of URL, e.g., http://www.example.com.
///  See also `requestUrl`
- (NSString *)baseUrl;

///  The URL path of request. This should only contain the path part of URL, e.g., /v1/user. See alse `baseUrl`.
///
///  @discussion This will be concated with `baseUrl` using [NSURL URLWithString:relativeToURL].
///              Because of this, it is recommended that the usage should stick to rules stated above.
///              Otherwise the result URL may not be correctly formed. See also `URLString:relativeToURL`
///              for more information.
///
///              Additionaly, if `requestUrl` itself is a valid URL, it will be used as the result URL and
///              `baseUrl` will be ignored.
- (NSString *)requestUrl;

/**
 设置请求的超时时间

 @return return value description
 */
- (NSTimeInterval)requestTimeoutInterval;

/**
 请求的参数

 @return return value description
 */
- (nullable id)requestArgument;

///  Override this method to filter requests with certain arguments when caching.
- (id)cacheFileNameFilterForRequestArgument:(id)argument;

/**
 请求类型，是get还是post

 @return return value description
 */
- (TYZRequestMethod)requestMethod;

/**
 请求的类型，是http，还是json请求

 @return return value description
 */
- (TYZRequestSerializerType)requestSerializerType;

/**
 请求后返回数据的类型

 @return return value description
 */
- (TYZResponseSerializerType)responseSerializerType;

///  Username and password used for HTTP authorization. Should be formed as @[@"Username", @"Password"].
- (nullable NSArray<NSString *> *)requestAuthorizationHeaderFieldArray;

/**
 设置头信息

 @return return value description
 */
- (nullable NSDictionary<NSString *, NSString *> *)requestHeaderFieldValueDictionary;

/**
 允许用户自定义NSURLRequest

 @return return value description
 */
- (nullable NSURLRequest *)buildCustomUrlRequest;

///  Whether the request is allowed to use the cellular radio (if present). Default is YES.
- (BOOL)allowsCellularAccess;

///  The validator will be used to test if `responseJSONObject` is correctly formed.
- (nullable id)jsonValidator;

///  This validator will be used to test if `responseStatusCode` is valid.
- (BOOL)statusCodeValidator;


/**
 解析数据
 */
- (void)handleParseData;

@end

NS_ASSUME_NONNULL_END
